//
//  ExPanoramaViewController.m
//  OC-Example
//
//  Created by lcj on 2020/12/31.
//  Copyright © 2020 apang.men. All rights reserved.
//

#import "ExPanoramaViewController.h"
#import <CoreMotion/CoreMotion.h>
#import <math.h>
#define ES_PI  (3.14159265f)
#define EX_MAX_RADIANS  110.0
#define EX_MIN_RADIANS  50.0

//https://github.com/danginsburg/opengles3-book/blob/master/Common/Source/esShapes.c#L75
/**
 根据numSlices和radius的大小取索引，顶点，纹理的值
 
 @param numSlices 球面切面数量
 @param radius 半径
 @param vertices 3维顶点坐标
 @param normals 法线
 @param texCoords 纹理
 @param indices 索引
 @param vertices_count 顶点点数
 @return 索引数量
 */
int esGenSphere ( int numSlices, float radius, GLfloat **vertices, GLfloat **normals,
                 GLfloat **texCoords, GLushort **indices, int * vertices_count)
{
    int i;
    int j;
    int numParallels = numSlices / 2;
    int numVertices = ( numParallels + 1 ) * ( numSlices + 1 );
    int numIndices = numParallels * numSlices * 6;
    float angleStep = ( 2.0f * ES_PI ) / ( ( float ) numSlices );
    
    // Allocate memory for buffers
    if ( vertices != NULL )
    {
        *vertices = malloc ( sizeof ( GLfloat ) * 3 * numVertices );
    }
    
    if ( normals != NULL )
    {
        *normals = malloc ( sizeof ( GLfloat ) * 3 * numVertices );
    }
    
    if ( texCoords != NULL )
    {
        *texCoords = malloc ( sizeof ( GLfloat ) * 2 * numVertices );
    }
    
    if ( indices != NULL )
    {
        *indices = malloc ( sizeof ( GLuint ) * numIndices );
    }
    
    for ( i = 0; i < numParallels + 1; i++ )
    {
        for ( j = 0; j < numSlices + 1; j++ )
        {
            int vertex = ( i * ( numSlices + 1 ) + j ) * 3;
            
            if ( vertices )
            {
                // 此处做了修改，0、1的值取反，修正图片翻转问题
                (*vertices)[vertex + 0] = - radius * sinf(angleStep * (float)i) * sinf(angleStep * (float)j);
                (*vertices)[vertex + 1] = - radius * cosf(angleStep * (float)i);
                (*vertices)[vertex + 2] = radius * sinf(angleStep * (float)i) * cosf(angleStep * (float)j);
            }
            
            if ( normals )
            {
                (*normals)[vertex + 0] = ( *vertices ) [vertex + 0] / radius;
                (*normals)[vertex + 1] = ( *vertices ) [vertex + 1] / radius;
                (*normals)[vertex + 2] = ( *vertices ) [vertex + 2] / radius;
            }
            
            if ( texCoords )
            {
                int texIndex = ( i * ( numSlices + 1 ) + j ) * 2;
                ( *texCoords ) [texIndex + 0] = ( float ) j / ( float ) numSlices;
                ( *texCoords ) [texIndex + 1] = 1.0f - ((float)i / (float)numParallels);
            }
        }
    }
    
    // Generate the indices
    if ( indices != NULL )
    {
        GLushort *indexBuf = ( *indices );
        
        for ( i = 0; i < numParallels ; i++ )
        {
            for ( j = 0; j < numSlices; j++ )
            {
                *indexBuf++  = i * ( numSlices + 1 ) + j;
                *indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + j;
                *indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + ( j + 1 );
                
                *indexBuf++ = i * ( numSlices + 1 ) + j;
                *indexBuf++ = ( i + 1 ) * ( numSlices + 1 ) + ( j + 1 );
                *indexBuf++ = i * ( numSlices + 1 ) + ( j + 1 );
            }
        }
    }
    if (vertices_count) {
        *vertices_count = numVertices;
    }
    return numIndices;
}
@interface ExPanoramaViewController ()
@property (nonatomic, assign) int drwaPointNum;
@property (nonatomic, strong) EAGLContext * glContext;
@property (nonatomic, strong) GLKBaseEffect * baseEffect;
@property (nonatomic, assign) CGFloat panX;
@property (nonatomic, assign) CGFloat panY;
@property (nonatomic, assign) CGFloat scale;
@property (nonatomic, assign) CGFloat ovyRadians;
@property (nonatomic, assign) BOOL motionEnable;
@property (nonatomic, strong) CMMotionManager * motionManager;
@property (nonatomic, strong) UIImage * image;
@end

@implementation ExPanoramaViewController

- (instancetype)initWithImage:(UIImage *)image {
    if (self = [super init]) {
        _image = image;
    }
    return self;
}

- (CMMotionManager *)motionManager {
    if (_motionManager == nil) {
        _motionManager = [[CMMotionManager alloc] init];
        _motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;
        _motionManager.showsDeviceMovementDisplay = NO;
    }
    return _motionManager;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    _panX = 0;
    _panY = 0;
    
    _scale = 1.0;
    _ovyRadians = 100;
    _motionEnable = NO;
    
    [self configGL];
    [self loadVerText];
    [self textureWithImage];
    UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
    [self.view addGestureRecognizer:pan];
    
    UIPinchGestureRecognizer * pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)];
    [self.view addGestureRecognizer:pinch];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)startMotion {
    _motionEnable = YES;
    [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical];
}

- (void)stopMotion {
    _motionEnable = NO;
    [self.motionManager stopDeviceMotionUpdates];
}

- (void)configGL {
    self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    GLKView *view = (GLKView *)self.view;
    view.context = self.glContext;
    /// 设置颜色格式和深度格式
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    self.preferredFramesPerSecond = 30;
    [EAGLContext setCurrentContext:self.glContext];
    glEnable(GL_DEPTH_TEST);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}

- (void)loadVerText {
    GLfloat *vertices = NULL;
    GLfloat *normals = NULL;
    GLfloat *texCoords = NULL;
    GLushort *indices = NULL;
    int numVertices = 0;
    _drwaPointNum = esGenSphere(200, 1.0f, &vertices, &normals, &texCoords, &indices, &numVertices);
    
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _drwaPointNum*sizeof(GLuint), indices, GL_STATIC_DRAW);
    
    GLuint vertex = 0;
    glGenBuffers(1, &vertex);
    glBindBuffer(GL_ARRAY_BUFFER, vertex);
    glBufferData(GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, NULL);
    
    GLuint normal;
    glGenBuffers(1, &normal);
    glBindBuffer(GL_ARRAY_BUFFER, normal);
    glBufferData(GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), normals, GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribNormal);
    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, NULL);
    
    GLuint texCoord;
    glGenBuffers(1, &texCoord);
    glBindBuffer(GL_ARRAY_BUFFER, texCoord);
    glBufferData(GL_ARRAY_BUFFER, numVertices*2*sizeof(GLfloat), texCoords, GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL);
}

- (void)textureWithImage {
    GLKTextureInfo * textureInfo;
    if (_image != nil) {
        textureInfo = [GLKTextureLoader textureWithCGImage:[_image CGImage] options:nil error:nil];
    } else {
        NSString *testPath = [[NSBundle mainBundle] pathForResource:@"test3" ofType:@"jpg" inDirectory:@"Resource/Document"];
        textureInfo = [GLKTextureLoader textureWithContentsOfFile:testPath options:nil error:nil];
    }
    
    self.baseEffect = [[GLKBaseEffect alloc] init];
    self.baseEffect.texture2d0.enabled = GL_TRUE;
    self.baseEffect.texture2d0.name = textureInfo.name;
    self.baseEffect.texture2d0.target = textureInfo.target;
    
    CGSize size = self.view.bounds.size;
    float aspect = fabs(size.width / size.height);
    
    //调整焦距
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(_ovyRadians), aspect, 0.1f, 400.0f);
    projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
    self.baseEffect.transform.projectionMatrix = projectionMatrix;
}

- (void)panAction:(UIPanGestureRecognizer *)gesture {
    CGPoint point = [gesture translationInView:self.view];
    self.panX += point.x;
    self.panY += point.y;
    //每次变换之后, 把改变值归零
    [gesture setTranslation:CGPointZero inView:self.view];
}

- (void)pinchAction:(UIPinchGestureRecognizer *)gesture {
    self.scale *= gesture.scale;
    gesture.scale = 1.0;
}

#pragma mark ---GLKViewDelegate, 绘制----

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    [self.baseEffect prepareToDraw];
    glDrawElements(GL_TRIANGLES, self.drwaPointNum, GL_UNSIGNED_SHORT, 0);
}

- (void)update {
    CGSize size = self.view.bounds.size;
    float aspect = fabs(size.width / size.height);
    CGFloat ovyRadians = 100 / self.scale;
    if (ovyRadians < EX_MIN_RADIANS) {
        ovyRadians = EX_MIN_RADIANS;
        self.scale = 1 / (EX_MIN_RADIANS/100);
    }
    if (ovyRadians > EX_MAX_RADIANS) {
        ovyRadians = EX_MAX_RADIANS;
        self.scale = 1 / (EX_MAX_RADIANS/100.0);
    }
    if (self.motionEnable) {
        GLKMatrix4 projectionMatrix;
        CMDeviceMotion *deviceMotion = self.motionManager.deviceMotion;
        double w = deviceMotion.attitude.quaternion.w;
        double wx = deviceMotion.attitude.quaternion.x;
        double wy = deviceMotion.attitude.quaternion.y;
        double wz = deviceMotion.attitude.quaternion.z;
        NSLog(@"w = %f, wx = %f, wy = %f wz = %f", w, wx, wy,wz);
        GLKQuaternion quaternion = GLKQuaternionMake(-wx, wy, wz, w);
        _ovyRadians = ovyRadians;
        projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(ovyRadians), aspect, 0.1f, 400.0f);
        GLKMatrix4 rotation = GLKMatrix4MakeWithQuaternion(quaternion);
        projectionMatrix = GLKMatrix4Multiply(projectionMatrix, rotation);
        projectionMatrix = GLKMatrix4RotateX(projectionMatrix, M_PI_2);
        self.baseEffect.transform.projectionMatrix = projectionMatrix;
    } else {
        if (ovyRadians != _ovyRadians) {
            _ovyRadians = ovyRadians;
            GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(ovyRadians), aspect, 0.1f, 400.0f);
            self.baseEffect.transform.projectionMatrix = projectionMatrix;
        }
    }
    
    if (self.panX != 0 || self.panY != 0 || self.scale != 1.0) {
        GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
        if (self.panY != 0) {
            modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, -0.005*self.panY);
        }
        if (self.panX != 0) {
            modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, 0.005*self.panX);
        }
        self.baseEffect.transform.modelviewMatrix = modelViewMatrix;
    }
}
@end
